package com.uow.algdemo.animation;

import java.util.List;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.widget.RelativeLayout;

import com.uow.algdemo.algorithms.Tree;
import com.uow.algdemo.material.Line;
import com.uow.algdemo.material.Node;
import com.uow.algdemo.material.Point;

/**
 * This class will receive a set of numbers for a tree and insertion animation
 * steps, then generate animation on animation layout.
 */
public class AnimationTree extends RelativeLayout implements AnimationInterface
{
	/**
     * This is handler for whole progress of animation.
     */
	private Handler handler;
	/**
     * This is current status of animation completed or not.
     */
	private boolean finished = false;
	/**
     * This is current status of pause or running.
     */
	private boolean pause = false;
    /**
     * This is the array of integers waiting for insert into tree.
     */
    private int[] array;
    /**
     * This array contains steps of a tree operation.
     */
    private List<int[][]> steps;
    /**
     * This is the current step of animation position.
     */
    private int position = 0;
    /**
     * This is the current speed of animation.
     */
    private int speed = 10;

    /**
     * This array contains view of integers of tree.
     */
    private Point[] numbers;
    /**
     * This array contains view of integers after sorting.
     */
    private Point[] numbers2;
	/**
     * This array contains coods of points.
     */
	private int[][] coods = new int[10][10];
    /**
     * This array contains view of edges between points.
     */
	private Line[] edges = new Line[10];
    /**
     * Animation type of different traverser method.
     */
	private int animationType;
    /**
     * Tree of current animation.
     */
	private Tree tr;
    /**
     * Size of a node.
     */
    private int size = 0;
    /**
     * Width of screen in pixel.
     */
    private int aWidth;
    /**
     * Height of screen in pixel.
     */
    private int aHeight;
	/**
     * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
     * @param newArray Array contains data for the tree.
     * @param width Width of layout in pixel.
     * @param height Height of layout in pixel.
     * @param psize Size of a point.
     * @param id Algorithm id.
     */
    public AnimationTree(Context context, int[] newArray, int width, int height, int psize, int id)
    {
		super(context);
		array = newArray;
		size = psize;
		aWidth = width;
		aHeight = height;
		RelativeLayout.LayoutParams params;
		numbers = new Point[array.length];
		numbers2 = new Point[array.length];
		tr = new Tree();

		for (int i = 0; i < array.length; i++)
		{
			Node tmp;
			if(id == 5)
			{
				tmp = tr.insertBinaryTree(array[i]);
			}
			else
			{
				tmp = tr.insertBinarySearchTree(array[i]);				
			}
		    numbers[i] = new Point(context, array[i], size);
		    numbers2[i] = new Point(context, array[i], size);
		    params = new RelativeLayout.LayoutParams(size, size);
			params.topMargin = aHeight / 10 + (tmp.level * (size + 50));
			params.leftMargin = (int)(aWidth * tmp.position) - psize / 2;
			this.addView(numbers[i], params);
    		coods[i][0] = aHeight / 10 + (tmp.level * (size + 50)) + psize / 2;
    		coods[i][1] = (int)(aWidth * tmp.position);
			edges[i] = new Line(AnimationTree.this.getContext(),coods[tmp.parent][1], coods[tmp.parent][0]);
			edges[i].setEnd(coods[i][1], coods[i][0]);
    		params = new RelativeLayout.LayoutParams(width, height);
    		edges[i].colorRed();
    		edges[i].invalidate();
    		this.addView(edges[i], 0, params);
		}
    }

	/**
	 * This method generate all points in traversed order.
     * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
     */
    public void printOrder(Context context)
    {
		for(int i=0; i<steps.size();i++)
		{
		    int[][] a=steps.get(i);
		    Point b=numbers2[a[1][0]];
		    b.setVisibility(4);
			RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(size, size);
			param.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
			param.bottomMargin = aHeight*5/100;
			param.leftMargin = ((aWidth - (array.length * (size + 10))) / 2) + (i * (size + 10));
			this.removeView(this);
			this.addView(b,param); 
		}
    }

    /**
     * @return The speed
     */
    public int getSpeed()
    {
    	return speed;
    }

    /**
     * This method set speed of animation.
     * @param newSpeed Speed value to set.
     */	
    public void setSpeed(int newSpeed)
    {
    	speed = newSpeed;
    }

    /**
     * This method generate animation and start animation.
     */
    public void runAnimation()
    {
    	switch(animationType)
    	{
    		case 0:
	    		tr.traversePreOrder(tr.root);
	        	this.steps = tr.getSteps();
	    		break;
	    	case 1:
	    		tr.traverseInOrder(tr.root);
	        	this.steps = tr.getSteps();
	    		break;
	    	case 2:
	    		tr.traversePostOrder(tr.root);
	        	this.steps = tr.getSteps();
	    		break;
    	}

		handler = new Handler()
		{
			public void handleMessage(Message msg)
			{
				switch (msg.what)
				{
					case 1:
					{
						if(pause == false && finished == false)
						{
							if(position < steps.size())
							{
								int[][] a = steps.get(position);
								if(a[0][0]==0)
								{
									highlight(a[1][0]);
								}
								position++;
							}
							else
							{
								finished = true;
							}
						}
						break;
					}
				}
				super.handleMessage(msg);
			}
		};

		handler.sendEmptyMessage(1);
    }

    /**
     * This method pause animation.
     */
    public void pauseAnimation()
    {
		pause = true;
    }

    /**
     * This method resume animation from pause position.
     */
    public void resumeAnimation()
    {
		pause = false;
    	handler.sendEmptyMessage(1);
    }

    /**
     * This method stop animation and clear layout.
     */
    public void stopAnimation()
    {
		finished = true;
    }

    /**
     * This method generate animation for highlight a point.
     * @param index Index of point to highlight in tree.
     */
    private void highlight(int index)
    {
    	numbers[index].colorGreen();
    	numbers2[index].colorGreen();
    	numbers2[index].setVisibility(0);
    	handler.sendEmptyMessageDelayed(1, speed * 50);
    	
    }
    /**
     * This method generate animation for change color back of a point.
     * @param index Index of point to change color back in tree.
     */
    private void colorBack(int index)
    {
    	numbers[index].colorBlue();
    }
    /**
     * This method show next step of animation when animation is pause.
     */	
    public void nextStep()
    {
		if(pause)
		{
			if(position < steps.size())
			{
				int[][] a = steps.get(position);
				if(a[0][0]==0)
				{
					highlight(a[1][0]);
				}
				position++;
			}
		}
    }
    /**
     * This method show last step of animation when animation is pause.
     */	
    public void lastStep()
    {
		if(pause)
		{
			if(position > 0)
			{
				position--;
				int[][] a = steps.get(position);
				if(a[0][0]==0)
				{
					colorBack(a[1][0]);
				}
			}
		}
    }

	/**
	 * @param steps The steps to set
	 */
    public void setSteps(List<int[][]> newSteps)
    {
		steps = newSteps;
    }
	/**
	 * @return The status
	 */
    public boolean getStatus()
    {
    	return finished;
    }
    /**
     * This method send feedback from this class for different command.
     * @param mes Message to current class.
     * @return An array for feedback information.
     */
    public int[][] sendMessage(int mes)
    {
    	animationType = mes;
		return null;
    }
}
